feat: event-driven extraction — Claude SessionEnd + opencode session.idle#79
Merged
Merged
Conversation
Fire `akm extract --type claude-code --session-id <id>` for the just-ended session so its durable insights reach the proposal queue in seconds instead of waiting for the periodic extract cron. - akm-hook.ts: new `extract-session` dispatch — reads the session_id from the SessionEnd stdin payload, skips transient terminations (reason clear/resume), and spawns extract detached + unref'd (never blocks session close). - plugin.json: register `extract-session` as a second SessionEnd hook. Idempotent + safe by design: `--session-id` respects the content-hash ledger (akm #602 / beta.33), so a re-fire or the periodic cron is a cheap skip with zero LLM calls — no `--force`. The cron remains the backstop for crashes that fire no hook. (opencode session.idle extraction intentionally deferred — needs a debounce/dedupe strategy to avoid flooding extract on every turn.) Tests: plugin.json registration + dispatch (clean no-op without akm on PATH; clear-reason skipped). Full suite green (326 → 328). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… gate) opencode has no true "session end" event and session.idle fires after every turn, so naive idle-hooking would re-extract each turn (each turn changes the content hash → not a cheap ledger skip). Option #3: min-interval gate. - On session.idle, extract the session at most once per AKM_EXTRACT_MIN_INTERVAL_MS (default 10 min) via detached `akm extract --type opencode --session-id <id>`. A turn-burst collapses to one periodic checkpoint; the content-hash ledger (akm-cli #602 / >=beta.33) further no-ops unchanged content for free. - Per-session `sessionLastExtractAt` Map (module-scope, long-lived plugin), cleared in the session.deleted cleanup. Configurable via env. - Only on session.idle (not compacted/deleted). Fire-and-forget so it never stalls a turn. The periodic extract cron remains the backstop for the final delta after the last turn (idle gives periodic checkpoints, not end-capture). Tradeoff vs the Claude SessionEnd one-shot: idle-gated extraction fires mid-session (more cost than a single end extract) but is the cleanest option opencode's event surface allows without a debounce timer — and is still a large improvement over cron-only latency. Tests: session.idle → one extract spawn; rapid second idle is gated. Full plugin suite green (328). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ilures) `runHookSandboxed` (akm-version-check) and `runHook` (claude-plugin) spread `...process.env` into the spawned hook, so ambient/CI env leaked in. CI runners set AKM_PLUGIN_NO_AUTO_DEFAULT=1, which flipped the first-run auto-default OFF — making the "auto-default-agent write" + "session-start injects curated context" tests pass locally but fail in CI (the tier2 red blocking PR #79). Strip inherited AKM_* vars before spawning so the sandbox depends only on what each test sets. Reproduced + verified: full suite is 328/0 both with and without AKM_PLUGIN_NO_AUTO_DEFAULT=1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
getAkmConfigPath() reads XDG_CONFIG_HOME. CI runners set it to a config that already has defaults.agent, so the first-run auto-default never fires — green locally (XDG_CONFIG_HOME unset), red in CI. Strip XDG_* from the inherited env and pin every XDG base dir to the sandbox tempDir so config reads are hermetic regardless of the ambient environment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
Eval diff
Metric deltas
Plugin surfaceNo surface changes. No regressions. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Event-driven session extraction for both providers, so durable insights reach the proposal queue in seconds/minutes instead of waiting for the periodic extract cron.
Claude —
SessionEnd(one-shot)akm-hook.ts: newextract-sessiondispatch — readssession_idfrom the SessionEnd stdin payload, skips transient terminations (clear/resume), spawns extract detached + unref'd.plugin.json: registersextract-sessionas a second SessionEnd hook.opencode —
session.idle(min-interval gate, Option #3)opencode has no true session-end event, and
session.idlefires after every turn (and each turn changes the content hash, so naive hooking would re-extract every turn). So:session.idle, extract at most once perAKM_EXTRACT_MIN_INTERVAL_MS(default 10 min) — a turn-burst collapses to one periodic checkpoint.sessionLastExtractAtmap (cleared onsession.deleted); detached spawn; configurable via env.Shared design
extract --session-idrespects the content-hash ledger (idempotent;--forceoverrides), so re-fires + the cron are cheap zero-LLM skips.dispose/server.instance.disposedare unverified on hard exit, so nothing relies on them.Tests
Claude: plugin.json registration + dispatch (clean no-op w/o akm on PATH; clear-reason skipped). opencode: session.idle → one extract spawn; rapid second idle gated. Full plugin suite green (328, 0 fail).
🤖 Generated with Claude Code